﻿using UnityEngine.InputSystem;

namespace GameBasic
{
    public struct ButtonInput
    {
        public const byte Flag_Active = 1;
        public const byte Flag_Trigger = 1 << 1;
        public const byte Flag_Press = 1 << 2;
        public const byte Flag_Release = 1 << 3;
        public const byte Flag_Click = 1 << 4;
        public const byte Flag_Hold = 1 << 5;
        public const byte Flag_HoldStart = 1 << 6;
        public const byte Flag_UIStart = 1 << 7;

        #region Properties
        public bool Active { get => (flags & Flag_Active) == Flag_Active;}
        public bool Trigger { get => (flags & Flag_Trigger) == Flag_Trigger;}
        public bool Press { get => (flags & Flag_Press) == Flag_Press;}
        public bool Release { get => (flags & Flag_Release) == Flag_Release;}
        public bool Click { get => (flags & Flag_Click) == Flag_Click; }
        public bool Hold { get => (flags & Flag_Hold) == Flag_Hold; }
        public bool HoldStart { get => (flags & Flag_HoldStart) == Flag_HoldStart; }
        public bool StartInUI { get => (flags & Flag_UIStart) == Flag_UIStart; }
        #endregion

        // Runtime
        public byte flags;
        public StepTimer holdTimer;

        public void Update(InputAction act, float deltaTime, bool ui = false)
        {
            int flagsOld = flags;
            int flagsNew = 0;

            // Trigger
            flagsNew |= act.triggered ? Flag_Trigger : 0;

            // Active
            int activeOld = flagsOld & 1;
            int activeNew = (int)act.ReadValue<float>();
            flagsNew |= activeNew;

            // Press, Release
            int press = (~activeOld & activeNew) & 1;
            int release = (activeOld & ~activeNew) & 1;
            flagsNew |= press << 2;
            flagsNew |= release << 3;

            // Click, Hold 
            {
                // Hold timer
                int reachHold = holdTimer.UpdateCeil(deltaTime) ? 1 : 0;
                // if not active, reset timer
                holdTimer.Reset(1 - activeNew);

                // click
                flagsNew |= (release & (1 - reachHold)) << 4;
                // hold
                int holdBit = (reachHold & activeOld) << 5;
                // holdStart: !holdOld & holdNew
                flagsNew |= (~flagsOld & holdBit & Flag_Hold) << 1;
                flagsNew |= holdBit;
            }

            // UI
            {
                int uiFlag = flagsOld >> 7 & 1;
                // clear if last frame released
                int releaseOld = flagsOld >> 3 & 1;
                uiFlag &= (1 - releaseOld);
                // mark on press
                uiFlag |= ui ? press : 0;
                flagsNew |= uiFlag << 7;
            }

            flags = (byte)flagsNew;
        }

        public bool All(byte flags)
        {
            return (this.flags & flags) == flags;
        }
    }
}